package appTool

import (
	"context"
	"fmt"
	"gitee.com/kinwyb/appTools/log"
	"github.com/rcrowley/go-metrics"
	"github.com/sirupsen/logrus"
	"github.com/smallnest/rpcx/client"
	rLog "github.com/smallnest/rpcx/log"
	"github.com/smallnest/rpcx/server"
	"go.etcd.io/etcd/client/v3"
	"net"
	"os"
	"strings"
	"time"
)

type RpcxService interface {
	// Services 返回服务地址
	Services(rpcxSev *server.Server, meta string)
}

// RpcxClient rpcx客户端接口
type RpcxClient interface {
	InitClients(discovery client.ServiceDiscovery, selector client.Selector)
	Close()
	SetLog(*logrus.Entry)
}

type AppRpcx struct {
	enableServer    bool
	addr            string
	etcds           []string
	etcdClient      *clientv3.Client
	basePath        string
	enableClient    bool
	server          *server.Server
	serverInitDown  chan struct{}
	services        []RpcxService
	clientDiscovery client.ServiceDiscovery
	clients         []RpcxClient
	isRun           bool
	selector        client.Selector
	serverMode      ServerMode
	registerPlugin  server.Plugin
	listener        net.Listener
	listenFile      *os.File //监听文件
}

func (a *AppRpcx) RegisterService(service RpcxService) *AppRpcx {
	if service != nil {
		a.services = append(a.services, service)
	}
	return a
}

func (a *AppRpcx) Shutdown(ctx context.Context) {
	a.isRun = false
	if a.server != nil {
		err := a.server.Shutdown(ctx)
		if err != nil {
			log.AppTool.Errorf("rpc服务关闭异常:%s", err.Error())
		}
	}
	if a.etcdClient != nil {
		a.etcdClient.Close()
	}
	if a.clientDiscovery != nil {
		a.clientDiscovery.Close()
		a.clientDiscovery = nil
	}
	for _, v := range a.clients {
		v.Close()
	}
	a.clients = nil
}

func (a *AppRpcx) EnableServer(addr string) *AppRpcx {
	a.enableServer = true
	a.addr = addr
	return a
}

func (a *AppRpcx) SetClientServiceDiscovery(discovery client.ServiceDiscovery) *AppRpcx {
	a.clientDiscovery = discovery
	return a
}

func (a *AppRpcx) SetNetListener(listener net.Listener) *AppRpcx {
	a.listener = listener
	return a
}

// SetRegisterPlugin 设置注册中心插件
func (a *AppRpcx) SetRegisterPlugin(plugin server.Plugin) *AppRpcx {
	a.registerPlugin = plugin
	return a
}

func (a *AppRpcx) SetServerPlugin(plugins ...server.Plugin) *AppRpcx {
	if len(plugins) > 0 {
		if a.server == nil {
			a.server = server.NewServer()
		}
		for _, plugin := range plugins {
			a.server.Plugins.Add(plugin)
		}
	}
	return a
}

// 检测相关服务路径时候存在,不存在的话就创建一个出来
func (a *AppRpcx) checkEtcdDiscovery() {
	if !strings.HasPrefix(a.basePath, "/") {
		a.basePath = "/" + a.basePath
	}
	resp, err := a.etcdClient.Get(context.Background(), a.basePath)
	if err == nil && resp.Count > 0 {
		return
	}
	// 创建相关路径
	a.etcdClient.Put(context.Background(), a.basePath, "")
}

func (a *AppRpcx) Client(obj ...RpcxClient) *AppRpcx {
	for _, c := range obj {
		if obj == nil {
			continue
		}
		a.enableClient = true
		a.clients = append(a.clients, c)
		if a.isRun {
			if a.clientDiscovery == nil {
				a.clientDiscovery = newEtcdV3DiscoveryTemplate(a.basePath, a.etcds, nil)
			}
			c.InitClients(a.clientDiscovery, a.selector)
			c.SetLog(log.Tag("rpc"))
		}
	}
	return a
}

func (a *AppRpcx) Run() {
	rLog.SetLogger(log.Tag("rpc"))
	if !strings.HasPrefix(a.basePath, "/") {
		a.basePath = "/" + a.basePath
	}
	a.isRun = true
	//开启客户端
	if a.enableClient {
		if a.selector == nil {
			a.selector = NewRpcServerModeSelect(a.serverMode)
		}
		if a.clientDiscovery == nil {
			a.checkEtcdDiscovery()
			a.clientDiscovery = newEtcdV3DiscoveryTemplate(a.basePath, a.etcds, nil)
		}
		for _, v := range a.clients {
			v.InitClients(a.clientDiscovery, a.selector)
			v.SetLog(log.Tag("rpc"))
		}
	}
	// 开启服务端
	if a.enableServer {
		if a.serverInitDown == nil {
			a.serverInitDown = make(chan struct{})
		}
		go a.runServer()
		<-a.serverInitDown
	}
}

func (a *AppRpcx) runServer() {
	if a.server == nil {
		a.server = server.NewServer()
	}
	if a.registerPlugin == nil {
		// 默认使用etcd注册中心
		r := &etcdV3RegisterPlugin{
			ServiceAddress: a.addr,
			EtcdServers:    a.etcds,
			BasePath:       a.basePath,
			Metrics:        metrics.NewRegistry(),
			UpdateInterval: time.Minute,
		}
		err := r.Start()
		if err != nil {
			log.AppTool.Fatalf("rpcx插件[EtcdRegisterPlugin]启动失败:%s", err.Error())
		}
		a.registerPlugin = r
	}
	// 附带服务器信息
	serverModeMeta := serverModeMetaKey + "=" + string(a.serverMode)
	a.server.Plugins.Add(a.registerPlugin)
	for _, v := range a.services {
		v.Services(a.server, serverModeMeta)
	}
	serverAddr := strings.Split(a.addr, "@")
	if len(serverAddr) < 2 {
		log.AppTool.Fatalf("rpcx监听地址异常: %s [eg: tcp@127.0.0.1:10001]", a.addr)
	}
	port := "10001"
	portString := strings.Split(serverAddr[1], ":")
	if len(portString) > 1 {
		port = portString[1]
	}
	close(a.serverInitDown) //通知初始化已经完成
	var err error
	if a.listener == nil {
		var listener net.Listener
		if a.listenFile != nil {
			listener, err = net.FileListener(a.listenFile)
			if err != nil {
				log.AppTool.WithError(err).Error("rpcx listen file error")
				listener = nil
			}
		}
		if listener == nil {
			listener, err = net.Listen(serverAddr[0], fmt.Sprintf("0.0.0.0:%s", port))
			if err != nil {
				log.AppTool.WithError(err).Fatal("rpcx监听异常")
			}
			if tcp, ok := listener.(*net.TCPListener); ok {
				a.listenFile, _ = tcp.File()
			}
		} else {
			log.AppTool.Info("rpc加载指定监听文件")
		}
		a.listener = listener
	}
	err = a.server.ServeListener(serverAddr[0], a.listener)
	if err != nil {
		log.AppTool.Errorf("rpcx监听结束:%v", err)
	} else {
		log.AppTool.Info("rpcx监听结束")
	}
}

// GetGlobalDiscovery 获取公共注册地址
func (a *AppRpcx) GetGlobalDiscovery() client.ServiceDiscovery {
	if a.clientDiscovery == nil {
		a.clientDiscovery = newEtcdV3DiscoveryTemplate(a.basePath, a.etcds, nil)
	}
	return a.clientDiscovery
}
